/*-------------------------------------------------------------------------
 *
 *	sha1.c
 *	  实现SHA1安全散列算法
 *
 * SHA1的后备实现，如RFC 3174中所述。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/common/sha1.c
 *
 *-------------------------------------------------------------------------
 */

/*	   $KAME: sha1.c,v 1.3 2000/02/22 14:01:18 itojun Exp $    */

/*
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *	  may be used to endorse or promote products derived from this software
 *	  without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
/*
 * FIPS pub 180-1: 安全散列算法 (SHA-1)
 * 基于: http://www.itl.nist.gov/fipspubs/fip180-1.htm
 * 由 Jun-ichiro itojun Itoh <itojun@itojun.org> 实现
 */

#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

#include <sys/param.h>

#include "sha1_int.h"

/* 常量表 */
static uint32 _K[] = {0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6};

#define K(t)	_K[(t) / 20]

#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d)))
#define F1(b, c, d) (((b) ^ (c)) ^ (d))
#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
#define F3(b, c, d) (((b) ^ (c)) ^ (d))

#define S(n, x)		(((x) << (n)) | ((x) >> (32 - (n))))

#define H(n)	(fc_ctx->h.b32[(n)])
#define COUNT	(fc_ctx->count)
#define BCOUNT	(fc_ctx->c.b64[0] / 8)
#define W(n)	(fc_ctx->m.b32[(n)])

#define PUTPAD(x) \
do { \
	fc_ctx->m.b8[(COUNT % 64)] = (x);		\
	COUNT++;				\
	COUNT %= 64;				\
	if (COUNT % 64 == 0)			\
		fc_sha1_step(fc_ctx);		\
} while (0)

static void fc_sha1_step(pg_sha1_ctx *fc_ctx)
{
	uint32		fc_a,
				fc_b,
				fc_c,
				fc_d,
				fc_e;
	size_t		fc_t,
				fc_s;
	uint32		fc_tmp;

#ifndef WORDS_BIGENDIAN
	pg_sha1_ctx fc_tctx;

	memmove(&fc_tctx.m.b8[0], &fc_ctx->m.b8[0], 64);
	fc_ctx->m.b8[0] = fc_tctx.m.b8[3];
	fc_ctx->m.b8[1] = fc_tctx.m.b8[2];
	fc_ctx->m.b8[2] = fc_tctx.m.b8[1];
	fc_ctx->m.b8[3] = fc_tctx.m.b8[0];
	fc_ctx->m.b8[4] = fc_tctx.m.b8[7];
	fc_ctx->m.b8[5] = fc_tctx.m.b8[6];
	fc_ctx->m.b8[6] = fc_tctx.m.b8[5];
	fc_ctx->m.b8[7] = fc_tctx.m.b8[4];
	fc_ctx->m.b8[8] = fc_tctx.m.b8[11];
	fc_ctx->m.b8[9] = fc_tctx.m.b8[10];
	fc_ctx->m.b8[10] = fc_tctx.m.b8[9];
	fc_ctx->m.b8[11] = fc_tctx.m.b8[8];
	fc_ctx->m.b8[12] = fc_tctx.m.b8[15];
	fc_ctx->m.b8[13] = fc_tctx.m.b8[14];
	fc_ctx->m.b8[14] = fc_tctx.m.b8[13];
	fc_ctx->m.b8[15] = fc_tctx.m.b8[12];
	fc_ctx->m.b8[16] = fc_tctx.m.b8[19];
	fc_ctx->m.b8[17] = fc_tctx.m.b8[18];
	fc_ctx->m.b8[18] = fc_tctx.m.b8[17];
	fc_ctx->m.b8[19] = fc_tctx.m.b8[16];
	fc_ctx->m.b8[20] = fc_tctx.m.b8[23];
	fc_ctx->m.b8[21] = fc_tctx.m.b8[22];
	fc_ctx->m.b8[22] = fc_tctx.m.b8[21];
	fc_ctx->m.b8[23] = fc_tctx.m.b8[20];
	fc_ctx->m.b8[24] = fc_tctx.m.b8[27];
	fc_ctx->m.b8[25] = fc_tctx.m.b8[26];
	fc_ctx->m.b8[26] = fc_tctx.m.b8[25];
	fc_ctx->m.b8[27] = fc_tctx.m.b8[24];
	fc_ctx->m.b8[28] = fc_tctx.m.b8[31];
	fc_ctx->m.b8[29] = fc_tctx.m.b8[30];
	fc_ctx->m.b8[30] = fc_tctx.m.b8[29];
	fc_ctx->m.b8[31] = fc_tctx.m.b8[28];
	fc_ctx->m.b8[32] = fc_tctx.m.b8[35];
	fc_ctx->m.b8[33] = fc_tctx.m.b8[34];
	fc_ctx->m.b8[34] = fc_tctx.m.b8[33];
	fc_ctx->m.b8[35] = fc_tctx.m.b8[32];
	fc_ctx->m.b8[36] = fc_tctx.m.b8[39];
	fc_ctx->m.b8[37] = fc_tctx.m.b8[38];
	fc_ctx->m.b8[38] = fc_tctx.m.b8[37];
	fc_ctx->m.b8[39] = fc_tctx.m.b8[36];
	fc_ctx->m.b8[40] = fc_tctx.m.b8[43];
	fc_ctx->m.b8[41] = fc_tctx.m.b8[42];
	fc_ctx->m.b8[42] = fc_tctx.m.b8[41];
	fc_ctx->m.b8[43] = fc_tctx.m.b8[40];
	fc_ctx->m.b8[44] = fc_tctx.m.b8[47];
	fc_ctx->m.b8[45] = fc_tctx.m.b8[46];
	fc_ctx->m.b8[46] = fc_tctx.m.b8[45];
	fc_ctx->m.b8[47] = fc_tctx.m.b8[44];
	fc_ctx->m.b8[48] = fc_tctx.m.b8[51];
	fc_ctx->m.b8[49] = fc_tctx.m.b8[50];
	fc_ctx->m.b8[50] = fc_tctx.m.b8[49];
	fc_ctx->m.b8[51] = fc_tctx.m.b8[48];
	fc_ctx->m.b8[52] = fc_tctx.m.b8[55];
	fc_ctx->m.b8[53] = fc_tctx.m.b8[54];
	fc_ctx->m.b8[54] = fc_tctx.m.b8[53];
	fc_ctx->m.b8[55] = fc_tctx.m.b8[52];
	fc_ctx->m.b8[56] = fc_tctx.m.b8[59];
	fc_ctx->m.b8[57] = fc_tctx.m.b8[58];
	fc_ctx->m.b8[58] = fc_tctx.m.b8[57];
	fc_ctx->m.b8[59] = fc_tctx.m.b8[56];
	fc_ctx->m.b8[60] = fc_tctx.m.b8[63];
	fc_ctx->m.b8[61] = fc_tctx.m.b8[62];
	fc_ctx->m.b8[62] = fc_tctx.m.b8[61];
	fc_ctx->m.b8[63] = fc_tctx.m.b8[60];
#endif

	fc_a = H(0);
	fc_b = H(1);
	fc_c = H(2);
	fc_d = H(3);
	fc_e = H(4);

	for (fc_t = 0; fc_t < 20; fc_t++)
	{
		fc_s = fc_t & 0x0f;
		if (fc_t >= 16)
			W(fc_s) = S(1, W((fc_s + 13) & 0x0f) ^ W((fc_s + 8) & 0x0f) ^ W((fc_s + 2) & 0x0f) ^ W(fc_s));
		fc_tmp = S(5, fc_a) + F0(fc_b, fc_c, fc_d) + fc_e + W(fc_s) + K(fc_t);
		fc_e = fc_d;
		fc_d = fc_c;
		fc_c = S(30, fc_b);
		fc_b = fc_a;
		fc_a = fc_tmp;
	}
	for (fc_t = 20; fc_t < 40; fc_t++)
	{
		fc_s = fc_t & 0x0f;
		W(fc_s) = S(1, W((fc_s + 13) & 0x0f) ^ W((fc_s + 8) & 0x0f) ^ W((fc_s + 2) & 0x0f) ^ W(fc_s));
		fc_tmp = S(5, fc_a) + F1(fc_b, fc_c, fc_d) + fc_e + W(fc_s) + K(fc_t);
		fc_e = fc_d;
		fc_d = fc_c;
		fc_c = S(30, fc_b);
		fc_b = fc_a;
		fc_a = fc_tmp;
	}
	for (fc_t = 40; fc_t < 60; fc_t++)
	{
		fc_s = fc_t & 0x0f;
		W(fc_s) = S(1, W((fc_s + 13) & 0x0f) ^ W((fc_s + 8) & 0x0f) ^ W((fc_s + 2) & 0x0f) ^ W(fc_s));
		fc_tmp = S(5, fc_a) + F2(fc_b, fc_c, fc_d) + fc_e + W(fc_s) + K(fc_t);
		fc_e = fc_d;
		fc_d = fc_c;
		fc_c = S(30, fc_b);
		fc_b = fc_a;
		fc_a = fc_tmp;
	}
	for (fc_t = 60; fc_t < 80; fc_t++)
	{
		fc_s = fc_t & 0x0f;
		W(fc_s) = S(1, W((fc_s + 13) & 0x0f) ^ W((fc_s + 8) & 0x0f) ^ W((fc_s + 2) & 0x0f) ^ W(fc_s));
		fc_tmp = S(5, fc_a) + F3(fc_b, fc_c, fc_d) + fc_e + W(fc_s) + K(fc_t);
		fc_e = fc_d;
		fc_d = fc_c;
		fc_c = S(30, fc_b);
		fc_b = fc_a;
		fc_a = fc_tmp;
	}

	H(0) = H(0) + fc_a;
	H(1) = H(1) + fc_b;
	H(2) = H(2) + fc_c;
	H(3) = H(3) + fc_d;
	H(4) = H(4) + fc_e;

	memset(&fc_ctx->m.b8[0], 0, 64);
}

static void fc_sha1_pad(pg_sha1_ctx *fc_ctx)
{
	size_t		fc_padlen;			/* 填充长度（字节） */
	size_t		fc_padstart;

	PUTPAD(0x80);

	fc_padstart = COUNT % 64;
	fc_padlen = 64 - fc_padstart;
	if (fc_padlen < 8)
	{
		memset(&fc_ctx->m.b8[fc_padstart], 0, fc_padlen);
		COUNT += fc_padlen;
		COUNT %= 64;
		fc_sha1_step(fc_ctx);
		fc_padstart = COUNT % 64;	/* 应该为 0 */
		fc_padlen = 64 - fc_padstart; /* 应该为 64 */
	}
	memset(&fc_ctx->m.b8[fc_padstart], 0, fc_padlen - 8);
	COUNT += (fc_padlen - 8);
	COUNT %= 64;
#ifdef WORDS_BIGENDIAN
	PUTPAD(fc_ctx->c.b8[0]);
	PUTPAD(fc_ctx->c.b8[1]);
	PUTPAD(fc_ctx->c.b8[2]);
	PUTPAD(fc_ctx->c.b8[3]);
	PUTPAD(fc_ctx->c.b8[4]);
	PUTPAD(fc_ctx->c.b8[5]);
	PUTPAD(fc_ctx->c.b8[6]);
	PUTPAD(fc_ctx->c.b8[7]);
#else
	PUTPAD(fc_ctx->c.b8[7]);
	PUTPAD(fc_ctx->c.b8[6]);
	PUTPAD(fc_ctx->c.b8[5]);
	PUTPAD(fc_ctx->c.b8[4]);
	PUTPAD(fc_ctx->c.b8[3]);
	PUTPAD(fc_ctx->c.b8[2]);
	PUTPAD(fc_ctx->c.b8[1]);
	PUTPAD(fc_ctx->c.b8[0]);
#endif
}

static void fc_sha1_result(uint8 *fc_digest0, pg_sha1_ctx *fc_ctx)
{
	uint8	   *fc_digest;

	fc_digest = (uint8 *) fc_digest0;

#ifdef WORDS_BIGENDIAN
	memmove(fc_digest, &ctx->h.b8[0], 20);
#else
	fc_digest[0] = fc_ctx->h.b8[3];
	fc_digest[1] = fc_ctx->h.b8[2];
	fc_digest[2] = fc_ctx->h.b8[1];
	fc_digest[3] = fc_ctx->h.b8[0];
	fc_digest[4] = fc_ctx->h.b8[7];
	fc_digest[5] = fc_ctx->h.b8[6];
	fc_digest[6] = fc_ctx->h.b8[5];
	fc_digest[7] = fc_ctx->h.b8[4];
	fc_digest[8] = fc_ctx->h.b8[11];
	fc_digest[9] = fc_ctx->h.b8[10];
	fc_digest[10] = fc_ctx->h.b8[9];
	fc_digest[11] = fc_ctx->h.b8[8];
	fc_digest[12] = fc_ctx->h.b8[15];
	fc_digest[13] = fc_ctx->h.b8[14];
	fc_digest[14] = fc_ctx->h.b8[13];
	fc_digest[15] = fc_ctx->h.b8[12];
	fc_digest[16] = fc_ctx->h.b8[19];
	fc_digest[17] = fc_ctx->h.b8[18];
	fc_digest[18] = fc_ctx->h.b8[17];
	fc_digest[19] = fc_ctx->h.b8[16];
#endif
}

/* 此 SHA1 实现的外部例程 */

/*
 * pg_sha1_init
 *
 * 初始化一个 SHA1 上下文。
 */
void pg_sha1_init(pg_sha1_ctx *fc_ctx)
{
	memset(fc_ctx, 0, sizeof(pg_sha1_ctx));
	H(0) = 0x67452301;
	H(1) = 0xefcdab89;
	H(2) = 0x98badcfe;
	H(3) = 0x10325476;
	H(4) = 0xc3d2e1f0;
}

/*
 * pg_sha1_update
 *
 * 更新一个 SHA1 上下文。
 */
void pg_sha1_update(pg_sha1_ctx *fc_ctx, const uint8 *fc_data, size_t fc_len)
{
	const uint8 *fc_input;
	size_t		fc_gaplen;
	size_t		fc_gapstart;
	size_t		fc_off;
	size_t		fc_copysiz;

	fc_input = (const uint8 *) fc_data;
	fc_off = 0;

	while (fc_off < fc_len)
	{
		fc_gapstart = COUNT % 64;
		fc_gaplen = 64 - fc_gapstart;

		fc_copysiz = (fc_gaplen < fc_len - fc_off) ? fc_gaplen : fc_len - fc_off;
		memmove(&fc_ctx->m.b8[fc_gapstart], &fc_input[fc_off], fc_copysiz);
		COUNT += fc_copysiz;
		COUNT %= 64;
		fc_ctx->c.b64[0] += fc_copysiz * 8;
		if (COUNT % 64 == 0)
			fc_sha1_step(fc_ctx);
		fc_off += fc_copysiz;
	}
}

/*
 * pg_sha1_final
 *
 * 完成一个 SHA1 上下文。
 */
void pg_sha1_final(pg_sha1_ctx *fc_ctx, uint8 *fc_dest)
{
	fc_sha1_pad(fc_ctx);
	fc_sha1_result(fc_dest, fc_ctx);
}
